#include "base.h"
#include "pbc/pbc.h"
#include "config.h"
#include "crypto_tool.h"
#include "basic_tool.h"
#include "randomnumber.h"
#include "mpz_tool.h"
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/timeb.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <pthread.h>
#include "error.h"

//#define CREATE_IBE_TABLE
#define MAX_RECORD 100000
#define MAX_BYTES_PER_LINE 10000
#define KEY_SIZE 128
#define MAX_THREAD 7
#define MAX_RECORD_THREAD 1
#define NUM_SEARCHABLE 45

bool NumberUsed[MAX_RECORD];
int g_remaining_random = MAX_RECORD;
size_t g_N = 0;
bool g_bRec = false, g_bRSA = false, g_bDSA = false, g_bIBE = false;

vector<map<string, unsigned long> > g_vecmap_rsa_fields;
vector<map<string, unsigned long> > g_vecmap_dsa_fields;
vector<map<string, unsigned long> > g_vecmapCounter;
vector<vector<int> > g_vec2dCounter; // int accounts for 4B
vector<string > g_vecAttributeName;
vector<string > g_vecLowerAttributeName;
vector<struct data> g_vecAllData;
vector<string> g_vecKey;
vector<int> g_vecIndex;			// to save space
RandomNumber g_rand;

mpz_t * mpz_d;
mpz_t * mpz_N;
mpz_t * mpz_p;
mpz_t * mpz_q;
mpz_t * mpz_g;
mpz_t * mpz_x;
mpz_t * mpz_p_minus_1_over_q;
pairing_t * g_pairing;
element_t * emt_P;
element_t * emt_Q;
element_t * emt_z;
element_t * emt_R;

struct item
{
  string strEncK_Ki;
  string strTag;
  int iIndex;
  string encIndex;
};

struct RecordThread
{
  size_t start;
  size_t end;
  size_t threadId;
};

struct data
{
  long ssn;
  vector<string > vecStrFields;
  vector<string > vecLowerStrFields;
  string rawdata;
};

vector<struct item> g_vecRsaShuffle;
vector<struct item> g_vecDsaShuffle;
vector<struct item> g_vecIBEShuffle;

vector<vector<struct item> > g_vecRsaThreadItem;
vector<vector<struct item> > g_vecDsaThreadItem;
vector<vector<struct item> > g_vecIBEThreadItem;

void shuffle(vector<int>& array)
{
  size_t n = array.size();
  if (n > 1) 
	{
	  size_t i;
	  for (i = 0; i < n - 1; i++) 
		{
		  size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
		  unsigned long t = array[j];
		  array[j] = array[i];
		  array[i] = t;
		}
    }
}


// shuffle an array
void shuffle(vector<struct item>& array)
{
  size_t n = array.size();
  if (n > 1) 
	{
	  size_t i;
	  for (i = 0; i < n - 1; i++) 
		{
		  size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
		  struct item t = array[j];
		  array[j] = array[i];
		  array[i] = t;
		}
    }
}

unsigned long NewRandom(int *array)
{
  static unsigned long j = 0;
  
  return array[j++];
}


int Init_Attribute(vector<string >& vat, string attfile)
{
  char buf[200];
  ifstream infile;
  infile.open(attfile.c_str());
  if (!infile)
	{
	  cerr<<"open "<<attfile<<" failed"<<endl;
	  return -1;
	}
  string att;
  while(infile.getline(buf, sizeof(buf)-1))
	{
	  att = buf;
	  vat.push_back(att);
	}
  return 0;
}


bool mycmp(const struct data& a, const struct data& b)
{
  return (a.ssn < b.ssn);		// sort in ascending order
}

void csvline_populate(vector<string> &record, const string& line, char delimiter)
{
    int linepos=0;
    int inquotes=false;
    char c;
    int linemax=line.length();
    string curstring;
    record.clear();
       
    while(line[linepos]!=0 && linepos < linemax)
    {
       
        c = line[linepos];
       
        if (!inquotes && curstring.length()==0 && c=='"')
        {
            //beginquotechar
            inquotes=true;
        }
        else if (inquotes && c=='"')
        {
            //quotechar
            if ( (linepos+1 <linemax) && (line[linepos+1]=='"') )
            {
                //encountered 2 double quotes in a row (resolves to 1 double quote)
                curstring.push_back(c);
                linepos++;
            }
            else
            {
                //endquotechar
                inquotes=false;
            }
        }
        else if (!inquotes && c==delimiter)
        {
            //end of field
            record.push_back( curstring );
            curstring="";
        }
        else if (!inquotes && (c=='\r' || c=='\n') )
        {
            record.push_back( curstring );
            return;
        }
        else
        {
            curstring.push_back(c);
        }
        linepos++;
    }
    record.push_back( curstring );
    return;
}

void ConvertAllDataToLower()
{
  for (size_t i = 0; i < g_vecAllData.size(); i++)
	{
	  g_vecAllData[i].vecLowerStrFields = g_vecAllData[i].vecStrFields;
	  for (size_t j = 0; j < g_vecAllData[i].vecLowerStrFields.size(); j++)
		{
		  BasicTool::ToLowercase(g_vecAllData[i].vecLowerStrFields[j]);
		}
	}
}

void * EncryptIBEThread(void * ptr)
{
  RecordThread * rt = (RecordThread *)ptr;
  struct item itm;
  char chash[HASH_LEN+1];
  element_t * emt_h = (element_t * ) malloc(sizeof(element_t) * 1);
  element_init_G2(*emt_h, *g_pairing);
  element_t * emt_t = (element_t * ) malloc(sizeof(element_t) * 1);
  element_init_GT(*emt_t, *g_pairing);

  unsigned char * data = new unsigned char [pairing_length_in_bytes_GT(*g_pairing)];
  string strKey, strAtVal, strSig, strKi, strEncK_Ki, strTag;
  for (size_t j = rt->start; j < rt->end; j++)
    {
      vector<string>& vecStrFields = g_vecAllData[j].vecLowerStrFields;
      for (size_t i = 0; i < NUM_SEARCHABLE; i++)
        {
		  int ctr = g_vec2dCounter[j][i];
		  strKey = g_vecKey[j]; // 16B
          // vecStrFields already in lower case;
          strAtVal = g_vecLowerAttributeName[i] + "=\"" + vecStrFields[i] + "\"";
		  // for IBE counter table
		  string hash = CryptoTool::Hash(strAtVal);
		  memcpy(chash, hash.c_str(), hash.length());
		  element_from_hash(*emt_h, chash, hash.length());
		  pairing_apply(*emt_t, *emt_Q, *emt_h, *g_pairing);
		  element_pow_zn(*emt_t, *emt_t, *emt_z);
		  int nb = element_to_bytes(data, *emt_t);
		  strSig.assign((char *)data, nb); // 128B

		  strSig = CryptoTool::Hash(strSig); // convert 128B to 16B
		  string hstrSigCounter = strSig + string((const char *)&ctr, sizeof(int));
          strKi = CryptoTool::HKey(hstrSigCounter);
          strEncK_Ki = CryptoTool::XOR(strKey, strKi);
          strTag = CryptoTool::HTag(hstrSigCounter);

          // store into rsa shuffle table
          itm.strEncK_Ki = strEncK_Ki;
          itm.strTag = strTag;
          itm.iIndex = g_vecIndex[j];
		  g_vecIBEThreadItem[rt->threadId].push_back(itm);
		}
	  if ((j - rt->start)%1000 == 0)
		{
		  cout<<"IBE thread "<<rt->threadId<<" encrypted "<<j - rt->start<<" records"<<endl;
		}
    }
  free(emt_h);
  free(emt_t);
  delete[] data;
  return NULL;
}

void * EncryptDSAThread(void * ptr)
{
  RecordThread * rt = (RecordThread *)ptr;
  struct item itm;
  string strKey, strAtVal, strSig, strKi, strEncK_Ki, strTag;
  mpz_t * mpzHashAtVal = (mpz_t * ) malloc(sizeof(mpz_t));
  mpz_t * mpzSig = (mpz_t * ) malloc(sizeof(mpz_t));
  mpz_init2( *mpzHashAtVal, 1024 );
  mpz_init2( *mpzSig, 1024 );
  for (size_t j = rt->start; j < rt->end; j++)
    {
      vector<string>& vecStrFields = g_vecAllData[j].vecLowerStrFields;
      for (size_t i = 0; i < NUM_SEARCHABLE; i++)
        {
		  int ctr = g_vec2dCounter[j][i];
		  strKey = g_vecKey[j]; // 16B
          // vecStrFields already in lower case;
          strAtVal = g_vecLowerAttributeName[i] + "=\"" + vecStrFields[i] + "\"";
		  // for DSA counter table
		  CryptoTool::FDH(mpzHashAtVal, strAtVal, mpz_p);
		  mpz_powm(*mpzHashAtVal, *mpzHashAtVal, *mpz_p_minus_1_over_q, *mpz_p);
		  mpz_powm(*mpzSig, *mpzHashAtVal, *mpz_x, *mpz_p);
		  strSig = MpzTool::String_from_mpz(NULL, mpzSig);

		  strSig = CryptoTool::Hash(strSig); // convert 128B to 16B
		  string hstrSigCounter = strSig + string((const char *)&ctr, sizeof(int));
          strKi = CryptoTool::HKey(hstrSigCounter);
          strEncK_Ki = CryptoTool::XOR(strKey, strKi);
          strTag = CryptoTool::HTag(hstrSigCounter);

          // store into rsa shuffle table
          itm.strEncK_Ki = strEncK_Ki;
          itm.strTag = strTag;
          itm.iIndex = g_vecIndex[j];
		  g_vecDsaThreadItem[rt->threadId].push_back(itm);
		}
	  if ((j - rt->start)%1000 == 0)
		{
		  cout<<"DSA thread "<<rt->threadId<<" encrypted "<<j - rt->start<<" records"<<endl;
		}
    }
  mpz_clear(*mpzHashAtVal);
  mpz_clear(*mpzSig);
  free(mpzHashAtVal);
  free(mpzSig);
  return NULL;
}

void * EncryptRSAThread(void * ptr)
{
  RecordThread * rt = (RecordThread *)ptr;
  struct item itm;
  mpz_t * mpzHashAtVal = (mpz_t * ) malloc(sizeof(mpz_t));
  mpz_t * mpzSig = (mpz_t * ) malloc(sizeof(mpz_t));
  mpz_init2( *mpzHashAtVal, 1024 );
  mpz_init2( *mpzSig, 1024 );
  string strKey, strAtVal, strSig, strKi, strEncK_Ki, strTag;
  for (size_t j = rt->start; j < rt->end; j++)
    {
      vector<string>& vecStrFields = g_vecAllData[j].vecLowerStrFields;
      for (size_t i = 0; i < NUM_SEARCHABLE; i++)
        {
		  int ctr = g_vec2dCounter[j][i];
		  strKey = g_vecKey[j]; // 16B
          // vecStrFields already in lower case;
          strAtVal = g_vecLowerAttributeName[i] + "=\"" + vecStrFields[i] + "\"";
          // for RSA counter table
		  string strHashAtVal = CryptoTool::Hash(strAtVal);
		  MpzTool::Mpz_from_string(mpzHashAtVal, strHashAtVal);
          mpz_powm(*mpzSig, *mpzHashAtVal, *mpz_d, *mpz_N);
          strSig = MpzTool::String_from_mpz(NULL, mpzSig);

		  strSig = CryptoTool::Hash(strSig); // convert 128B to 16B
		  string hstrSigCounter = strSig + string((const char *)&ctr, sizeof(int));
          strKi = CryptoTool::HKey(hstrSigCounter);
          strEncK_Ki = CryptoTool::XOR(strKey, strKi);
          strTag = CryptoTool::HTag(hstrSigCounter);

          // store into rsa shuffle table
          itm.strEncK_Ki = strEncK_Ki;
          itm.strTag = strTag;
          itm.iIndex = g_vecIndex[j];
          g_vecRsaThreadItem[rt->threadId].push_back(itm);      
        }
	  if ((j - rt->start)%1000 == 0)
		{
		  cout<<"RSA thread "<<rt->threadId<<" encrypted "<<j - rt->start<<" records"<<endl;
		}
    }
  mpz_clear(*mpzHashAtVal);
  mpz_clear(*mpzSig);
  free(mpzHashAtVal);
  free(mpzSig);
  return NULL;
}

void EncryptTable(string strTable)
{
  pthread_t threadId[MAX_THREAD];
  RecordThread pRtTable[MAX_THREAD];
  size_t seg = (size_t) ceil(g_vecAllData.size() / (double) MAX_THREAD);
  for (size_t i = 0; i < MAX_THREAD; i++)
    {
      pRtTable[i].start = i * seg;
      pRtTable[i].end = pRtTable[i].start + seg;
      if (pRtTable[i].end > g_vecAllData.size())
        {
          pRtTable[i].end = g_vecAllData.size();
        }
	  pRtTable[i].threadId = i;
      void *(*start_routine)(void*) = NULL;
      if (strTable == "RSA")
        {
          start_routine = EncryptRSAThread;
        }
      else if (strTable == "DSA")
        {
          start_routine = EncryptDSAThread;
        }
      else if (strTable == "IBE")
        {
          start_routine = EncryptIBEThread;
        }
      pthread_create(&threadId[i], NULL, start_routine, (void*)&pRtTable[i]);
    }
  vector<struct item> * pTable = NULL;
  ofstream outfile;
  string filename;
  for (size_t i = 0; i < MAX_THREAD; i++)
    {
      pthread_join(threadId[i], NULL);
	  if (strTable == "RSA")
		{
		  pTable = &g_vecRsaShuffle;
		  filename = Config::m_output_database_dir + "rsa_ssn_counter_table.dat";
		  g_vecRsaShuffle.insert(g_vecRsaShuffle.end(), g_vecRsaThreadItem[i].begin(), g_vecRsaThreadItem[i].end());
		  g_vecRsaThreadItem[i].clear();
		}
	  else if (strTable == "DSA")
		{
		  pTable = &g_vecDsaShuffle;
		  filename = Config::m_output_database_dir + "dsa_ssn_counter_table.dat";
		  g_vecDsaShuffle.insert(g_vecDsaShuffle.end(), g_vecDsaThreadItem[i].begin(), g_vecDsaThreadItem[i].end());
		  g_vecDsaThreadItem[i].clear();
		}
	  else if (strTable == "IBE")
		{
		  pTable = &g_vecIBEShuffle;
		  filename = Config::m_output_database_dir + "ibe_ssn_counter_table.dat";
		  g_vecIBEShuffle.insert(g_vecIBEShuffle.end(), g_vecIBEThreadItem[i].begin(), g_vecIBEThreadItem[i].end());
		  g_vecIBEThreadItem[i].clear();
		}
    }
  shuffle(*pTable);
  outfile.open(filename.c_str());
  vector<struct item>::iterator it;
  for (it = (*pTable).begin(); it != (*pTable).end(); ++it)
	{
	  size_t len = it->strTag.length();
	  outfile.write((const char *)&len, sizeof(size_t));
	  outfile.write(it->strTag.c_str(), len);
	  len = it->strEncK_Ki.length();
	  outfile.write((const char *)&len, sizeof(size_t));
	  outfile.write(it->strEncK_Ki.c_str(), len);
	  outfile.write((const char *)&(it->iIndex), sizeof(int));
	}
  outfile.close();
  double t = BasicTool::F_Time(BasicTool::STOP);
  cout<<t/1000<<"s finished "<<strTable<<" table"<<endl;
}

void * EncryptRecordThread(void * ptr)
{
  RecordThread * rt = (RecordThread *)ptr;
  for (size_t i = rt->start; i < rt->end; i++)
    {
      stringstream ss;
      vector<string>& vecStrFields = g_vecAllData[i].vecStrFields;
      size_t uiAttributeNum = vecStrFields.size();
      for (size_t j = 0; j < uiAttributeNum; j++)
        {
          if (j >= NUM_SEARCHABLE && j < 51) // needs to be changed if database field is changed
            {
              // load file
              string strAttFile = Config::m_original_database_dir + vecStrFields[j];
              string strAttContent;
              if (BasicTool::Load_File(strAttContent, strAttFile))
				{
				  cout<<"try again"<<endl;
				  if (BasicTool::Load_File(strAttContent, strAttFile))
					{
					  cout<<"quit encryption"<<endl;
					  return NULL;
					}
				}

              ss<<g_vecAttributeName[j]<<'['<<strAttContent.length()<<']'<<strAttContent;
            }
          else
            {
              ss<<g_vecAttributeName[j]<<'['<<vecStrFields[j].length()<<']'<<
				vecStrFields[j]; // 
            }
        }
      string strCompleteRec = ss.str();
      string strEncRec = CryptoTool::RC4(strCompleteRec, g_vecKey[i]);

      // determine the storing folder (do i need to create the folder?)
      //iNewIndex = NewRandom(random_indices);
      int iNewIndex = g_vecIndex[i];
      ss.str("");
      ss<<Config::m_output_database_dir<<iNewIndex/5000<<'/'<<iNewIndex%5000;

      string strEncDataFile;
      strEncDataFile = ss.str(); // store encrecord

      //cout<<"start to dump encrypted record"<<endl;
      if (BasicTool::Dump_File(strEncRec, strEncDataFile) )
        {
          cerr<<"dump file error"<<endl;
          return NULL;
        }
	  if ((i - rt->start)%1000 == 0)
		{
		  double t = BasicTool::F_Time(BasicTool::STOP);
		  cout<<t/1000<<"s thread "<<rt->threadId<<" finished encrypting "<<i - rt->start<<" record"<<endl;
		}
    }
  return NULL;
}


void EncryptAllRecords(string strDatabaseFilename)
{
  string strRawRec;
  double t;
  // sort database by ssn and store raw data in sorted_people.csv
  ifstream infile(strDatabaseFilename.c_str());
  if (!infile)
	{
	  cerr<<"open "<<strDatabaseFilename<<" failed"<<endl;
	  return;
	}

  vector<string > vecStrFields;
  while (getline(infile, strRawRec) && infile.good())
	{
	  csvline_populate(vecStrFields, strRawRec, ',');
	  struct data da;
	  da.ssn = atoi(vecStrFields[10].c_str());
	  da.vecStrFields = vecStrFields;
      da.rawdata = strRawRec;
	  g_vecAllData.push_back(da);
	}
  infile.close();
  g_N = g_vecAllData.size();
  sort (g_vecAllData.begin(), g_vecAllData.end(), mycmp);
  t = BasicTool::F_Time(BasicTool::STOP);
  cout<<t/1000<<"s finished sorting "<<endl;

  // create index table
  g_vecIndex.resize(g_N);
  for (size_t i = 0; i < g_N; i++)
	{
	  g_vecIndex[i] = i;
	}
  shuffle(g_vecIndex);

  // create key table
  g_vecKey.resize(g_N);
  mpz_t * mpz_key = Memreuse::New();
  for (size_t i = 0; i < g_N; i++)
	{
      g_rand.Get_Z_Bits(mpz_key, KEY_SIZE);
      string strKey = MpzTool::String_from_mpz(NULL, mpz_key);
      g_vecKey[i] = strKey;
	}
  Memreuse::Delete(mpz_key);

  // create counter table
  g_vec2dCounter.resize(g_N);
  g_vecmapCounter.resize(NUM_SEARCHABLE);
  for (size_t i = 0; i < g_N; i++)
    {
      vecStrFields = g_vecAllData[i].vecStrFields; // in normal case now
      g_vec2dCounter[i].resize(NUM_SEARCHABLE);
      for (int k = 0; k < NUM_SEARCHABLE; k++)
        {
          BasicTool::ToLowercase(vecStrFields[k]); // does not differentiate upper lower case
          g_vec2dCounter[i][k] = (g_vecmapCounter[k])[CryptoTool::Hash(vecStrFields[k])] ++;
        }
    }
  t = BasicTool::F_Time(BasicTool::STOP);
  cout<<t/1000<<"s finished counter table "<<endl;
  // store sorted database
  string strSortedDatabase = Config::m_original_database_dir + "sorted_people.csv";
  ofstream outfile(strSortedDatabase.c_str());
  for (vector<struct data>::iterator it = g_vecAllData.begin(); it != g_vecAllData.end();
	   ++it)
	{
	  outfile<<it->rawdata<<endl;
	}
  outfile.close();

  // store keytable
  string keytablefile = Config::m_original_database_dir + "key_table.dat";
  FILE * pfile = fopen(keytablefile.c_str(), "w+");
  fprintf(pfile, "%ld\n", g_vecKey.size());
  mpz_t * tmp = Memreuse::New();
  for (size_t i = 0; i < g_vecKey.size(); i++)
    {
	  MpzTool::Mpz_from_string(tmp, g_vecKey[i]);
	  gmp_fprintf(pfile, "%Zx\n", *tmp);
    }
  Memreuse::Delete(tmp);
  fclose(pfile);

  // store indextable
  string indextablefile = Config::m_original_database_dir + "index_table.dat";
  outfile.open(indextablefile.c_str());
  outfile<<g_vecIndex.size()<<endl;
  for (size_t i = 0; i < g_vecIndex.size(); i++)
    {
      outfile<<g_vecIndex[i]<<endl;
    }
  outfile.close();

  // store counter table
  string counterfile = Config::m_original_database_dir + "counter_table.dat";
  outfile.open(counterfile.c_str());
  outfile<<g_vec2dCounter.size()<<" "<<g_vec2dCounter[0].size()<<endl;
  for (size_t i = 0; i < g_N; i++)
    {
	  size_t m = g_vec2dCounter[i].size();
      for (size_t j = 0; j < m; j++)
        {
          outfile<<g_vec2dCounter[i][j]<<" ";
        }
      outfile<<endl;
    }
  outfile.close();
  cout<<"tables saved, start encryption"<<endl;

  // encrypt records
  pthread_t threadId[MAX_RECORD_THREAD];
  RecordThread pRtTable[MAX_RECORD_THREAD];
  size_t seg = (size_t) ceil(g_vecAllData.size() / (double) MAX_RECORD_THREAD);
  for (size_t i = 0; i < MAX_RECORD_THREAD; i++)
    {
      pRtTable[i].start = i * seg;
      pRtTable[i].end = pRtTable[i].start + seg;
      if (pRtTable[i].end > g_vecAllData.size())
        {
          pRtTable[i].end = g_vecAllData.size();
        }
	  pRtTable[i].threadId = i;
      pthread_create(&threadId[i], NULL, EncryptRecordThread, (void*)&pRtTable[i]);
    }

  ConvertAllDataToLower();      // convert g_vecAllData to lowercase

  if (g_bRSA)
    {
      EncryptTable("RSA");
    }

  if (g_bDSA)
    {
      EncryptTable("DSA");
    }

  if(g_bIBE)
    {
      EncryptTable("IBE");
    }
    
  for (size_t i = 0; i < MAX_RECORD_THREAD; i++)
    {
      pthread_join(threadId[i], NULL); // encrypt record is slow, so meanwhile encrypt attribute
    }
  t = BasicTool::F_Time(BasicTool::STOP);
  cout<<t/1000<<"s finished encrypting database "<<endl;

}

void ReadSortedDataTable()
{
  vector<string > vecStrFields;
  string strRawRec;
  string strSortedDatabase = Config::m_original_database_dir + "sorted_people.csv";
  ifstream infile(strSortedDatabase.c_str());
  if (!infile)
    {
      cerr<<"read sorted_people.csv error"<<endl;
    }
  while (getline(infile, strRawRec) && infile.good())
	{
	  csvline_populate(vecStrFields, strRawRec, ',');
	  struct data da;
	  da.ssn = atoi(vecStrFields[10].c_str());
	  da.vecStrFields = vecStrFields;
      da.rawdata = strRawRec;
	  g_vecAllData.push_back(da);
	}
}

void ReadKeyTable()
{
  g_vecKey.clear();
  string keytablefile = Config::m_original_database_dir + "key_table.dat";
  FILE * pfile = fopen(keytablefile.c_str(), "r");
  size_t n;
  int ret = fscanf(pfile, "%ld\n", &n);
  if (ret <= 0)
	{
	  perror("fscan key_table.dat");
      return;
	}
  g_vecKey.resize(n);
  mpz_t * tmp = Memreuse::New();
  for (size_t i = 0; i < n; i++)
    {
	  gmp_fscanf(pfile, "%Zx\n", *tmp);
	  g_vecKey[i] = MpzTool::String_from_mpz(NULL, tmp);
    }
  Memreuse::Delete(tmp);
}

void ReadIndexTable()
{
  g_vecIndex.clear();
  string indextablefile = Config::m_original_database_dir + "index_table.dat";
  ifstream infile(indextablefile.c_str());
  if (!infile)
    {
      cerr<<"read index_table.dat error"<<endl;
    }
  int n;
  infile>>n;
  g_vecIndex.resize(n);
  int index;
  for (int i = 0; i < n; i++)
	{
	  infile>>index;
	  g_vecIndex[i] = index;
	}
}

void ReadCounterTable()
{
  g_vec2dCounter.clear();
  string counterfile = Config::m_original_database_dir + "counter_table.dat";
  ifstream infile(counterfile.c_str());
  if (!infile)
    {
      cerr<<"read counter_table.dat error"<<endl;
    }

  int n, m;
  infile>>n>>m;
  g_vec2dCounter.resize(n);
  for (size_t i = 0; i < g_vecAllData.size(); i++)
    {
      g_vec2dCounter[i].resize(m);
      for (int k = 0; k < m; k++)
        {
          infile>>g_vec2dCounter[i][k];
        }
    }
}

int main(int argc, const char * argv[])
{
  int iTable = 0;               // it is a bitmap

  cout<<"[15]: Encrypt Everything, [1]: Record only, [2]: RSA table only, [4]: DSA table only, [8]: IBE table only"<<endl;
  cin>>iTable;
  if ((iTable & 1) > 0)
    {
      g_bRec = true;
      cout<<"Encrypt Records; ";
    }
  if ((iTable & (1<<1)) > 0)
    {
      g_bRSA = true;
      cout<<"RSA table; ";
    }
  if ((iTable & (1<<2)) > 0)
    {
      g_bDSA = true;
      cout<<"DSA table; ";
    }
  if ((iTable & (1<<3)) > 0)
    {
      g_bIBE = true;
      cout<<"IBE table; ";
    }
  cout<<endl;

  string configfilename = "config.ini";
  Config::Input(configfilename);

  string strDatabaseFilename = Config::m_original_database;
  string strAttributeFilename = "attribute.dat";

  string strRSAPrivFile = Config::m_key_dir + "rsa.priv";
  string strDSAPrivFile = Config::m_key_dir + "dsa.priv";
  string strIBEPubFile = Config::m_key_dir + "ibe.server";
  Memreuse::Initialize(1000, 1024);
  // read private key
  mpz_d = Memreuse::New();
  mpz_N = Memreuse::New();
  mpz_p = Memreuse::New();
  mpz_q = Memreuse::New();
  mpz_g = Memreuse::New();
  mpz_x = Memreuse::New();
  mpz_p_minus_1_over_q = Memreuse::New();

  g_pairing = (pairing_t *) malloc(sizeof(pairing_t));
  emt_P = Memreuse::NewElement();
  emt_Q = Memreuse::NewElement();
  emt_z = Memreuse::NewElement();
  emt_R = Memreuse::NewElement();

  g_vecRsaThreadItem.resize(MAX_THREAD);
  g_vecDsaThreadItem.resize(MAX_THREAD);
  g_vecIBEThreadItem.resize(MAX_THREAD);

  BasicTool::Load_RSA_Priv_Key(mpz_d, mpz_N, strRSAPrivFile);
  BasicTool::Load_DSA_Priv_Key(mpz_p, mpz_q, mpz_g, mpz_x, strDSAPrivFile);
  BasicTool::Load_IBE_Server_Key(g_pairing, emt_P, emt_Q, emt_z, emt_R, strIBEPubFile);
  Memreuse::InitializeElements(1000, g_pairing);

  mpz_sub_ui(*mpz_p_minus_1_over_q, *mpz_p, 1);
  mpz_divexact(*mpz_p_minus_1_over_q, *mpz_p_minus_1_over_q, *mpz_q);

  // read attribute names
  if (Init_Attribute(g_vecAttributeName, strAttributeFilename))
	return -1;

  g_vecLowerAttributeName = g_vecAttributeName; // lower case version
  for (size_t i = 0; i < g_vecLowerAttributeName.size(); i++)
	{
	  BasicTool::ToLowercase(g_vecLowerAttributeName[i]);
	}

  // create necessary folder
  string foldername = Config::m_output_database_dir;
  cout<<"create "<<foldername<<endl;
  int ret = mkdir(foldername.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  if (ret < 0)
    {
      perror("mkdir");
    }

  BasicTool::F_Time(BasicTool::START);

  if (g_bRec)
    {
      EncryptAllRecords(strDatabaseFilename);
    }
  else
    {
      ReadSortedDataTable();
	  ConvertAllDataToLower();
      ReadKeyTable();
	  ReadIndexTable();
      ReadCounterTable();
	  if (g_bRSA)
		{
		  EncryptTable("RSA");
		}

	  if (g_bDSA)
		{
		  EncryptTable("DSA");
		}

	  if (g_bIBE)
		{
		  EncryptTable("IBE");
		}
    }
  return 0;
}
